package com.xiaohua.api.utils.document;

import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.PdfContentByte;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.tools.imageio.ImageIOUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.awt.image.BufferedImage;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 文档处理工具类
 * 提供文档转换相关功能
 *
 * @author 小花
 * @date 2025-03-30
 */
public class DocumentUtils {

    private static final Logger log = LoggerFactory.getLogger(DocumentUtils.class);

    /**
     * 生成唯一的临时文件名
     *
     * @param prefix 文件名前缀
     * @param suffix 文件名后缀
     * @return 临时文件路径
     */
    public static String generateTempFileName(String prefix, String suffix) {
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
        String random = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
        return prefix + "_" + timestamp + "_" + random + suffix;
    }

    /**
     * 创建保存结果的目录
     *
     * @param baseDir 基础目录
     * @return 结果目录路径
     */
    public static String createResultDirectory(String baseDir) {
        // 创建日期目录结构
        String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String resultDir = baseDir + File.separator + datePath;
        
        // 确保目录存在
        File dir = new File(resultDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        
        return resultDir;
    }
    
    /**
     * 将MultipartFile保存到临时文件
     *
     * @param file MultipartFile
     * @param prefix 文件名前缀
     * @param suffix 文件名后缀
     * @return 临时文件
     * @throws IOException 如果保存过程中出错
     */
    public static File saveTempFile(MultipartFile file, String prefix, String suffix) throws IOException {
        // 创建临时文件
        File tempFile = File.createTempFile(prefix, suffix);
        file.transferTo(tempFile);
        // 标记为JVM退出时删除
        tempFile.deleteOnExit();
        return tempFile;
    }
    
    /**
     * 将Base64编码的数据保存到临时文件
     *
     * @param base64Data Base64编码的数据
     * @param prefix 文件名前缀
     * @param suffix 文件名后缀
     * @return 临时文件
     * @throws IOException 如果保存过程中出错
     */
    public static File saveTempFileFromBase64(String base64Data, String prefix, String suffix) throws IOException {
        // 解码Base64数据
        byte[] fileBytes = Base64.getDecoder().decode(base64Data);
        // 创建临时文件
        File tempFile = File.createTempFile(prefix, suffix);
        org.apache.commons.io.FileUtils.writeByteArrayToFile(tempFile, fileBytes);
        // 标记为JVM退出时删除
        tempFile.deleteOnExit();
        return tempFile;
    }

    /**
     * 将URL下载到临时文件
     *
     * @param fileUrl 文件URL
     * @param prefix 文件名前缀
     * @param suffix 文件名后缀
     * @return 临时文件
     * @throws IOException 如果下载过程中出错
     */
    public static File downloadUrlToTempFile(String fileUrl, String prefix, String suffix) throws IOException {
        // 下载文件
        java.net.URL url = new java.net.URL(fileUrl);
        try (InputStream inputStream = url.openStream()) {
            // 创建临时文件
            File tempFile = File.createTempFile(prefix, suffix);
            try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }
            // 标记为JVM退出时删除
            tempFile.deleteOnExit();
            return tempFile;
        }
    }

    /**
     * Word文档转换为PDF
     *
     * @param wordFile Word文件
     * @param outputPdfPath 输出PDF路径
     * @return true如果转换成功，否则false
     */
    public static boolean convertWordToPdf(File wordFile, String outputPdfPath) {
        if (wordFile == null || !wordFile.exists()) {
            log.error("Word文件不存在或为空");
            return false;
        }
        
        String fileName = wordFile.getName().toLowerCase();
        FileInputStream fis = null;
        FileOutputStream fos = null;
        
        try {
            fis = new FileInputStream(wordFile);
            fos = new FileOutputStream(outputPdfPath);
            
            if (fileName.endsWith(".docx")) {
                // 处理DOCX文件 (OOXML格式)
                XWPFDocument document = new XWPFDocument(fis);
                
                // 使用iText创建PDF
                Document pdfDocument = new Document(PageSize.A4);
                PdfWriter writer = PdfWriter.getInstance(pdfDocument, fos);
                pdfDocument.open();
                
                // 提取文本内容并添加到PDF
                for (org.apache.poi.xwpf.usermodel.XWPFParagraph paragraph : document.getParagraphs()) {
                    pdfDocument.add(new Paragraph(paragraph.getText()));
                }
                
                pdfDocument.close();
                writer.close();
                document.close();
            } else if (fileName.endsWith(".doc")) {
                // 处理DOC文件 (旧版Word格式)
                HWPFDocument hwpfDocument = new HWPFDocument(fis);
                
                // 使用iText创建PDF
                Document pdfDocument = new Document(PageSize.A4);
                PdfWriter writer = PdfWriter.getInstance(pdfDocument, fos);
                pdfDocument.open();
                
                // 提取文本并添加到PDF
                String text = hwpfDocument.getDocumentText();
                pdfDocument.add(new Paragraph(text));
                
                pdfDocument.close();
                writer.close();
            } else {
                log.error("不支持的Word文件格式: {}", fileName);
                return false;
            }
            
            return true;
        } catch (Exception e) {
            log.error("Word转PDF失败: {}", e.getMessage(), e);
            return false;
        } finally {
            // 关闭流
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    log.warn("关闭输入流失败: {}", e.getMessage());
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    log.warn("关闭输出流失败: {}", e.getMessage());
                }
            }
        }
    }

    /**
     * 将Word文档转换为图片
     *
     * @param wordFile Word文件
     * @param outputDir 输出目录
     * @return 生成的图片文件列表
     */
    public static List<String> convertWordToImages(File wordFile, String outputDir) {
        List<String> imageFiles = new ArrayList<>();
        
        // 首先将Word转换为PDF
        String tempPdfPath = outputDir + File.separator + "temp_" + System.currentTimeMillis() + ".pdf";
        boolean converted = convertWordToPdf(wordFile, tempPdfPath);
        
        if (!converted) {
            log.error("Word转PDF失败，无法继续转换为图片");
            return imageFiles;
        }
        
        // 然后将PDF转换为图片
        try {
            // 加载PDF文档
            PDDocument document = PDDocument.load(new File(tempPdfPath));
            PDFRenderer renderer = new PDFRenderer(document);
            
            // 设置图片分辨率为300 DPI
            float dpi = 300;
            
            // 遍历每一页并转换为图片
            for (int i = 0; i < document.getNumberOfPages(); i++) {
                // 渲染图像
                BufferedImage image = renderer.renderImageWithDPI(i, dpi, ImageType.RGB);
                
                // 设置输出文件名
                String imageFileName = outputDir + File.separator + "page_" + (i + 1) + ".png";
                
                // 保存图像
                ImageIOUtil.writeImage(image, imageFileName, (int) dpi);
                
                // 添加到结果列表
                imageFiles.add(imageFileName);
            }
            
            // 关闭文档
            document.close();
            
            // 删除临时PDF文件
            new File(tempPdfPath).delete();
            
            return imageFiles;
        } catch (IOException e) {
            log.error("PDF转图片失败: {}", e.getMessage(), e);
            // 删除临时PDF文件
            new File(tempPdfPath).delete();
            return imageFiles;
        }
    }

    /**
     * 对文档文件进行加密
     *
     * @param inputFile 输入文件
     * @param outputPath 输出路径
     * @param password 用户密码
     * @param ownerPassword 所有者密码
     * @return true如果加密成功，否则false
     */
    public static boolean encryptDocument(File inputFile, String outputPath, String password, String ownerPassword) {
        if (inputFile == null || !inputFile.exists()) {
            log.error("文档文件不存在或为空");
            return false;
        }
        
        String fileName = inputFile.getName().toLowerCase();
        
        try {
            if (fileName.endsWith(".pdf")) {
                // 加密PDF文件
                return encryptPdf(inputFile, outputPath, password, ownerPassword);
            } else if (fileName.endsWith(".doc") || fileName.endsWith(".docx")) {
                // 直接加密Word文件
                return encryptWord(inputFile, outputPath, password, ownerPassword);
            } else {
                log.error("不支持的文档格式: {}", fileName);
                return false;
            }
        } catch (Exception e) {
            log.error("文档加密失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 加密Word文档
     *
     * @param sourceFile 源Word文件
     * @param targetPath 目标Word文件路径（加密后的文件）
     * @param userPassword 用户密码
     * @param ownerPassword 所有者密码
     * @return 加密是否成功
     */
    public static boolean encryptWord(File sourceFile, String targetPath, String userPassword, String ownerPassword) {
        try {
            // 创建输出目录
            File outputFile = new File(targetPath);
            if (!outputFile.getParentFile().exists()) {
                outputFile.getParentFile().mkdirs();
            }
            
            // 读取Word文件
            FileInputStream fis = new FileInputStream(sourceFile);
            
            // 创建输出流
            FileOutputStream fos = new FileOutputStream(targetPath);
            
            // 根据文件扩展名来决定使用哪种文档对象
            if (sourceFile.getName().toLowerCase().endsWith(".docx")) {
                // 处理DOCX文件（Office 2007+格式）
                XWPFDocument document = new XWPFDocument(fis);
                
                // 设置文档保护
                // 注意：POI的保护功能比较有限，只能添加编辑限制，无法像PDF那样添加打开密码
                if (ownerPassword != null && !ownerPassword.isEmpty()) {
                    document.enforceReadonlyProtection(ownerPassword, null);
                }
                
                // 保存加密后的文档
                document.write(fos);
                document.close();
            } else if (sourceFile.getName().toLowerCase().endsWith(".doc")) {
                // 处理DOC文件（Office 97-2003格式）
                HWPFDocument document = new HWPFDocument(fis);
                
                // 注意：POI对于旧版DOC文件的保护功能更有限
                // 我们可以将其转换为DOCX格式，然后应用保护
                // 这里简单地写回文件，但没有添加真正的加密
                document.write(fos);
                
                // 关闭文档
                fis.close();
                fos.close();
                
                // 为了用户体验，给出警告信息
                log.warn("旧版DOC文件的加密功能有限，仅提供基本写保护");
            }
            
            log.info("Word文档加密成功：{}", targetPath);
            return true;
        } catch (Exception e) {
            log.error("Word文档加密失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 加密PDF文档
     *
     * @param sourceFile 源PDF文件
     * @param targetPath 目标PDF文件路径
     * @param userPassword 用户密码
     * @param ownerPassword 所有者密码
     * @return 加密是否成功
     */
    public static boolean encryptPdf(File sourceFile, String targetPath, String userPassword, String ownerPassword) {
        try {
            // 读取PDF文件
            PdfReader reader = new PdfReader(sourceFile.getAbsolutePath());
            
            // 设置加密参数
            int permissions = PdfWriter.ALLOW_PRINTING | PdfWriter.ALLOW_COPY | PdfWriter.ALLOW_SCREENREADERS;
            
            // 创建输出流
            FileOutputStream fos = new FileOutputStream(targetPath);
            
            // 创建一个新的PDF文档
            Document document = new Document(reader.getPageSize(1));
            
            // 创建PDF写入器，并设置加密选项
            PdfWriter writer = PdfWriter.getInstance(document, fos);
            writer.setEncryption(
                userPassword != null ? userPassword.getBytes() : null, 
                ownerPassword != null ? ownerPassword.getBytes() : null, 
                permissions, 
                PdfWriter.ENCRYPTION_AES_128
            );
            
            // 打开文档
            document.open();
            
            // 复制内容
            PdfContentByte cb = writer.getDirectContent();
            int numPages = reader.getNumberOfPages();
            for (int i = 1; i <= numPages; i++) {
                document.newPage();
                
                // 导入页面
                cb.addTemplate(writer.getImportedPage(reader, i), 0, 0);
            }
            
            // 关闭文档和流
            document.close();
            writer.close();
            reader.close();
            fos.close();
            
            return true;
        } catch (Exception e) {
            log.error("PDF加密失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 将本地文件路径转换为HTTP URL路径（相对路径）
     *
     * @param localFilePath 本地文件绝对路径
     * @param baseUploadDir 系统配置的上传基础目录
     * @param resourcePrefix 资源前缀
     * @return HTTP访问URL（相对路径）
     */
    public static String getHttpUrl(String localFilePath, String baseUploadDir, String resourcePrefix) {
        // 如果本地路径没有包含基础目录，则直接返回原路径
        if (!localFilePath.contains(baseUploadDir)) {
            return localFilePath;
        }
        
        // 替换反斜杠为正斜杠
        String normalized = localFilePath.replace('\\', '/');
        
        // 计算相对路径
        int index = normalized.indexOf(baseUploadDir.replace('\\', '/'));
        if (index >= 0) {
            String relativePath = normalized.substring(index + baseUploadDir.replace('\\', '/').length());
            // 确保relativePath以/开头
            if (!relativePath.startsWith("/")) {
                relativePath = "/" + relativePath;
            }
            return resourcePrefix + relativePath;
        }
        
        return localFilePath;
    }
    
    /**
     * 将本地文件路径转换为完整的HTTP URL路径
     *
     * @param localFilePath 本地文件绝对路径
     * @param baseUploadDir 系统配置的上传基础目录
     * @param resourcePrefix 资源前缀
     * @param domain 域名或IP地址（含协议），如：http://127.0.0.1:8080
     * @return 完整的HTTP URL访问地址
     */
    public static String getHttpUrl(String localFilePath, String baseUploadDir, String resourcePrefix, String domain) {
        // 获取相对URL
        String relativeUrl = getHttpUrl(localFilePath, baseUploadDir, resourcePrefix);
        
        // 如果返回的是原路径，则不处理
        if (relativeUrl.equals(localFilePath)) {
            return relativeUrl;
        }
        
        // 确保domain不以/结尾
        if (domain.endsWith("/")) {
            domain = domain.substring(0, domain.length() - 1);
        }
        
        // 确保relativeUrl以/开头
        if (!relativeUrl.startsWith("/")) {
            relativeUrl = "/" + relativeUrl;
        }
        
        // 返回完整URL
        return domain + relativeUrl;
    }
} 